<template>
  <div @mousedown="handleMouseDown">
    <div :class="[$style.widget, { 'opacity-75': isMoving }]" :style="widgetPosition">
      <slot :isEditing="isEditing" :isMoving="isMoving" />
      <div class="absolute px-1" :style="actionsPosition" style="background: rgba(0,0,0,0.01);">
        <div :class="$style.actionsPanel">
          <button v-if="hideable" @click="hide"
            :class="$style.action">{{ t('widget.hide') }}</button>
          <button v-if="!readonly" @click="toggleEdit"
            :class="[$style.action, { [$style.active]: isEditing }]">{{ t('widget.edit') }}</button>
          <button v-if="moveHandles !== 'none'" @click="toggleMove"
            :class="[$style.action, { [$style.active]: isMoving }]">{{ t('widget.move') }}</button>
          <button v-if="removable" @mousedown="startRemoveTimer" @mouseup="cancelRemoveTimer" @mouseleave="cancelRemoveTimer"
            :class="[$style.action, $style.removable, { [$style.removing]: isRemoving }]">{{ t('widget.delete') }}</button>
        </div>
      </div>
      <div v-if="isMoving">
        <div v-if="isHandleShown('tl')" :class="$style.mover" @mousedown="startMove('tl', $event)" style="left: -0.5rem; top: -0.5rem;"></div>
        <div v-if="isHandleShown('tc')" :class="$style.mover" @mousedown="startMove('tc', $event)" style="left: calc(50%  - 0.5rem); top: -0.5rem;"></div>
        <div v-if="isHandleShown('tr')" :class="$style.mover" @mousedown="startMove('tr', $event)" style="left: calc(100% - 0.5rem); top: -0.5rem;"></div>
        <div v-if="isHandleShown('cr')" :class="$style.mover" @mousedown="startMove('cr', $event)" style="left: calc(100% - 0.5rem); top: calc(50% - 0.5rem);"></div>
        <div v-if="isHandleShown('br')" :class="$style.mover" @mousedown="startMove('br', $event)" style="left: calc(100% - 0.5rem); top: calc(100% - 0.5rem);"></div>
        <div v-if="isHandleShown('bc')" :class="$style.mover" @mousedown="startMove('bc', $event)" style="left: calc(50%  - 0.5rem); top: calc(100% - 0.5rem);"></div>
        <div v-if="isHandleShown('bl')" :class="$style.mover" @mousedown="startMove('bl', $event)" style="left: -0.5rem; top: calc(100% - 0.5rem);"></div>
        <div v-if="isHandleShown('cl')" :class="$style.mover" @mousedown="startMove('cl', $event)" style="left: -0.5rem; top: calc(50%  - 0.5rem);"></div>
        <div v-if="isHandleShown('cc')" :class="$style.mover" @mousedown="startMove('cc', $event)" style="left: calc(50%  - 0.5rem); top: calc(50%  - 0.5rem);"></div>
      </div>
    </div>
    <div v-if="isMoving" :class="[$style.mover, $style.active]" :style="moverPosition" @mousedown="startMove(config.anchor.pos, $event)"></div>
  </div>
</template>

<script lang="ts">
import { defineComponent, PropType, computed, inject, ref } from 'vue'
import { Widget, Anchor, WidgetManager } from './interfaces'
import { useI18n } from 'vue-i18n'

function useRemovable (remove: () => void) {
  const isRemoving = ref(false)
  let tmid: ReturnType<typeof setTimeout> | null = null

  function startRemoveTimer () {
    isRemoving.value = true
    tmid = setTimeout(remove, 1000)
  }
  function cancelRemoveTimer () {
    isRemoving.value = false
    if (tmid !== null) {
      clearTimeout(tmid)
      tmid = null
    }
  }

  return { startRemoveTimer, cancelRemoveTimer, isRemoving }
}

export default defineComponent({
  props: {
    config: {
      type: Object as PropType<Widget & { anchor: Anchor }>,
      required: true
    },
    moveHandles: {
      type: [String, Array] as PropType<string | string[]>,
      default: undefined
    },
    readonly: {
      type: Boolean,
      default: false
    },
    removable: {
      type: Boolean,
      default: true
    },
    hideable: {
      type: Boolean,
      default: true
    },
    inlineEdit: {
      type: Boolean,
      default: true
    }
  },
  setup (props) {
    const wm = inject<WidgetManager>('wm')!

    const moverPosition = computed(() => {
      const { anchor, wmZorder } = props.config
      return {
        'top': `max(0%, min(calc(${anchor.y}% - (1rem/2)), calc(100% - 1rem)))`,
        'left': `max(0%, min(calc(${anchor.x}% - (1rem/2)), calc(100% - 1rem)))`,
        'z-index': (typeof wmZorder === 'number') ? wmZorder : undefined
      }
    })

    const widgetPosition = computed(() => {
      const { anchor, wmZorder } = props.config

      // <top, center, bottom><left, center, right>
      let translate: string | undefined
      let max = { w: 100, h: 100 }
      if (anchor.pos === 'tl') {
        translate = undefined
        max = { w: 100 - anchor.x, h: 100 - anchor.y }
      } else if (anchor.pos === 'tc') {
        translate = 'translate(-50%, 0%)'
        max = { w: 2 * Math.min(100 - anchor.x, anchor.x), h: 100 - anchor.y }
      } else if (anchor.pos === 'tr') {
        translate = 'translate(-100%, 0%)'
        max = { w: anchor.x, h: 100 - anchor.y }
      } else if (anchor.pos === 'cr') {
        translate = 'translate(-100%, -50%)'
        max = { w: anchor.x, h: 2 * Math.min(100 - anchor.y, anchor.y) }
      } else if (anchor.pos === 'br') {
        translate = 'translate(-100%, -100%)'
        max = { w: anchor.x, h: anchor.y }
      } else if (anchor.pos === 'bc') {
        translate = 'translate(-50%, -100%)'
        max = { w: 2 * Math.min(100 - anchor.x, anchor.x), h: anchor.y }
      } else if (anchor.pos === 'bl') {
        translate = 'translate(0%, -100%)'
        max = { w: 100 - anchor.x, h: anchor.y }
      } else if (anchor.pos === 'cl') {
        translate = 'translate(0%, -50%)'
        max = { w: 100 - anchor.x, h: 2 * Math.min(100 - anchor.y, anchor.y) }
      } else if (anchor.pos === 'cc') {
        translate = 'translate(-50%, -50%)'
        max = { w: 2 * Math.min(100 - anchor.x, anchor.x), h: 2 * Math.min(100 - anchor.y, anchor.y) }
      }

      return {
        'top': `${anchor.y}%`,
        'left': `${anchor.x}%`,
        'max-width': `${max.w}%`,
        'max-height': `${max.h}%`,
        'transform': translate,
        'z-index': (typeof wmZorder === 'number') ? wmZorder : undefined
      }
    })

    const actionsPosition = computed(() => {
      const { anchor } = props.config

      if (anchor.x <= 50 && anchor.y <= 50) {
        return {
          top: '0',
          left: '100%'
        }
      }
      if (anchor.x >= 50 && anchor.y <= 50) {
        return {
          top: '0',
          right: '100%'
        }
      }
      if (anchor.x >= 50 && anchor.y >= 50) {
        return {
          bottom: '0',
          right: '100%'
        }
      }
      if (anchor.x <= 50 && anchor.y >= 50) {
        return {
          bottom: '0',
          left: '100%'
        }
      }
    })

    const shownHandles = computed(() => {
      if (Array.isArray(props.moveHandles)) {
        return props.moveHandles
      }
      if (!props.moveHandles) {
        return ['tl', 'tc', 'tr', 'cr', 'br', 'bc', 'bl', 'cl', 'cc']
      }
      if (props.moveHandles === 'center') {
        return ['cc']
      }
      if (props.moveHandles === 'corners') {
        return ['tl', 'tr', 'br', 'bl']
      }
      if (props.moveHandles === 'top-bottom') {
        return ['tl', 'tc', 'tr', 'br', 'bc', 'bl']
      }
      return []
    })

    function startMove (pos: string, e: MouseEvent) {
      props.config.anchor.pos = pos
      updatePosition(e)
      document.addEventListener('mousemove', updatePosition)
      document.addEventListener('mouseup', endMove)

      function endMove () {
        document.removeEventListener('mousemove', updatePosition)
        document.removeEventListener('mouseup', endMove)
      }
    }

    function updatePosition (e: MouseEvent) {
      props.config.anchor.x = Math.min(Math.max(e.clientX / window.innerWidth, 0), 1) * 100
      props.config.anchor.y = Math.min(Math.max(e.clientY / window.innerHeight, 0), 1) * 100
    }

    const isEditing = ref(false)
    const isMoving = ref(false)

    const { t } = useI18n()

    return {
      t,
      moverPosition,
      widgetPosition,
      actionsPosition,
      handleMouseDown () {
        // @TODO: why null check?
        if (props.config.wmId != null) {
          wm.bringToTop(props.config.wmId)
        }
      },
      startMove,
      isMoving,
      isEditing,
      isHandleShown (pos: string) {
        return props.config.anchor.pos !== pos &&
          shownHandles.value.includes(pos)
      },
      hide () {
        wm.hide(props.config.wmId)
      },
      toggleEdit () {
        isMoving.value = false
        if (props.inlineEdit) {
          isEditing.value = !isEditing.value
        } else {
          const settings = wm.widgets.value.find(w => w.wmType === 'settings')!
          wm.setFlag(settings.wmId, `settings:widget:${props.config.wmId}`, true)
          wm.show(settings.wmId)
        }
      },
      toggleMove () {
        isMoving.value = !isMoving.value
        isEditing.value = false
      },
      ...useRemovable(() => {
        wm.remove(props.config.wmId)
      })
    }
  }
})
</script>

<style lang="postcss" module>
.widget {
  position: absolute;
  display: flex;

  &:not(:hover) {
    .actionsPanel {
      display: none;
    }
  }
}

.actionsPanel {
  @apply py-1;
  color: #fff;
  background: rgba(0,0,0, 0.4);
  display: flex;
  flex-direction: column;
  white-space:nowrap;
  @apply rounded;
}

.action {
  @apply px-1;
  text-align: left;
  white-space: nowrap;

  &:hover {
    background: rgba(255,255,255, 0.15);
  }

  &.active {
    background: rgba(0,0,0, 0.6);
  }

  &.removable {
    background: linear-gradient(to left, transparent 50%, theme('colors.red.600') 50%);
    background-size: 250% 100%;
    background-position: right bottom;
  }

  &.removing {
    background-size: 200% 100%;
    transition: background-position 1s ease-out;
    background-position: left bottom;
  }
}

.mover {
  position: absolute;
  /* top: max(0%, min(calc(y% - (1rem/2)), calc(100% - 1rem))); */
  /* left: max(0%, min(calc(x% - (1rem/2)), calc(100% - 1rem))); */
  width: 1rem;
  height: 1rem;
  border: 0.25rem solid rgba(0, 0, 0, 0.6);
  background: rgba(0,0,0,0.2);
  cursor: move;
  user-select: none;

  &.active {
    position: fixed;
    border-color: #fff;
    box-shadow: 0 1px 3px 0 rgb(0, 0, 0),
                0 1px 2px 0 rgb(0, 0, 0);
  }
}
</style>

<style lang="postcss">
.widget-default-style {
  @apply rounded;
  @apply bg-gray-900;
  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.75),
              0 1px 2px 0 rgba(0, 0, 0, 0.75);
}
</style>
